home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / VideoToolbox 96.06.15 / VideoToolboxSources / WindowToEPS.c < prev    next >
Text File  |  1995-07-27  |  14KB  |  352 lines

  1. /*
  2. WindowToEPS.c
  3. Copyright © 1994,1995 Denis G. Pelli
  4.  
  5. WindowToEPS(window,filename,rectPtr,pageRectPtr,cellsPerInch,grayLevels,reflectance);
  6.  
  7. WindowToEPS converts a grayscale image (in a window or GWorld) to a PostScript
  8. file that a LaserWriter or Linotype will accurately render on paper. The output
  9. file is a standard Encapsulated PostScript File, with file type 'EPSF', commonly
  10. referred to as an "eps" file. Most word processors, e.g. Word and PageMaker, know
  11. how to import eps files. Macintoshes only understand QuickDraw, not PostScript,
  12. so they don't know what image the PostScript code would produce. That's why the
  13. eps file includes a PICT resource that provides an on-screen preview, so the
  14. image will look approximately right on your monitor screen, e.g. in Word. To
  15. import in Word, use the Insert:Picture or Insert:File command. Once imported, you
  16. can get Word to rescale the image by dragging the image's lower right corner
  17. while holding down the shift key. Try the VideoToolbox demo Grating.
  18.  
  19. Bear in mind that on-screen you're looking at the PICT, whereas, when you print
  20. on a LaserWriter, the image is produced by the PostScript code. When in your word
  21. processor, the on-screen images will look best if you use the Monitors control
  22. panel to set your monitor to 256 Grays, but this won't affect printing.
  23.  
  24. The filename, by convention, should end in ".eps" to indicate that it's an
  25. encapsulated postscript file, but this is not enforced. The file's type is set to
  26. 'EPSF' with creator 'R*ch'. (This creator corresponds to BBEdit, so
  27. double-clicking will open it as a text file in BBEdit, which includes a
  28. Special:SendPostScript command for downloading poscript images to the
  29. laserwriter. You can change the creator to be anything you want.)
  30.  
  31. The supplied PixMap must have 8 bits per pixel. The window's color table is
  32. ignored. The raw pixel value (which Apple calls an "index") is transformed by the
  33. optional reflectance array (if present), and then sent directly to the printer.
  34. If "reflectance" is NULL, then the pixel value, from 0 to 255 is interpreted by
  35. PostScript as proportional to desired reflectance, from zero to 1. If you supply
  36. a "reflectance" array the values should range from 0.0 to 1.0. The *rectPtr
  37. indicates what part of your PixMap is to be used.
  38.  
  39. The color table attached to the PICT resource, which provides the on-screen preview
  40. in word processors, is created to match the PostScript one, using the reflectance
  41. array if supplied, otherwise creating a gray color table running from black to white.
  42.  
  43. The *pageRectPtr is subtle. It describes, in typographers points (1/72"), the
  44. rectangle that your image will be mapped onto on the printed page. It is
  45. essential that you keep in mind that Apple and Adobe use different coordinate
  46. systems. Both Apple and Adobe increase x from left to right. However, Apple has y
  47. increasing from top to bottom, whereas Adobe increases y from bottom to top.
  48. Adobe's origin is the lower left corner of the page, even though that point is
  49. usually not printable, since most printers can only print to within about a half
  50. inch of the edge. The pageRect, though supplied in Apple's Rect data structure,
  51. must be in Adobe's coordinates, respecting the names of the Rect structure's
  52. fields: left, top, right, bottom. So, for an image to fill most of an 8.5x11
  53. page, with 0.5" margins, you might use the following:
  54.  
  55.         SetRect(&pageRect,0.5*72,10.5*72,8*72,0.5*72);
  56.  
  57. In printing PostScript halftones the halftone cell size determines
  58. both the spatial and graylevel resolutions of the resulting image. For
  59. the convenience of the user this can be specified by setting either the
  60. cellsPerInch or the grayLevels argument to the desired value; the other
  61. one should be zero. If both are zero then the printer will be left at
  62. its default cell size, which is usually a good choice. Note that there
  63. need not be any particular correspondence between pixels in your image
  64. and cells in the halftone; the printer automatically resamples your
  65. image to produce the halftone.
  66.  
  67. If you set cellsPerInch to a nonzero value then the printer will be
  68. asked to print its halftone with that many halftone cells per inch. E.g.
  69. to produce a halftone original for subsequent one-to-one reproduction in
  70. a journal, you'll want the cells to be coarse enough for them to
  71. reproduce without re-screening, e.g. 100 cells per inch.
  72.  
  73. Alternatively, if you set grayLevels to a nonzero value then the
  74. printer will be asked to print its halftone with cells containing
  75. grayLevels-1 printer pixels, yielding the specified number of gray
  76. levels. E.g. you might want to force your 300 dpi LaserWriter to use big
  77. cells yielding 256 gray levels.
  78.  
  79. Here's a minimal example:
  80.  
  81.         WindowToEPS(window,"test.eps",&rect,&pageRect,0.0,0,NULL);
  82.  
  83.     To print a screen to disk, preserving the size and scale of the
  84. image, try this:
  85.  
  86.         pageRect=window->portRect;
  87.         pageRect.top*=-1;        // convert from Apple to Adobe coordinates
  88.         pageRect.bottom*=-1;    // convert from Apple to Adobe coordinates
  89.         SetRect(&paperRect,0,11*72,8.5*72,0);
  90.         CenterRectInRect(&pageRect,&paperRect);
  91.         WindowToEPS(window,"test.eps",&window->portRect,&pageRect,0,0,NULL);
  92.  
  93.     Tiling is something we often want to do, creating a huge image by
  94. taping many pages together. Simply import your EPS file into a good
  95. graphics program, e.g. PageMaker, and print with tiling.
  96.  
  97. REFERENCES
  98.  
  99. Adobe Systems (1985) PostScript Language Reference Manual, Second Edition. 
  100. Reading, MA: Addison-Wesley.
  101.  
  102. Pelli, D. G. (1987) Programming in PostScript: Imaging on paper from a mathematical
  103. description. BYTE, 12 (5), 185-202.
  104.  
  105. HISTORY:
  106. 4/21/91    dgp    wrote it
  107. 7/24/91 dgp added comment about shifting pageRect
  108. 8/24/91    dgp    Made compatible with THINK C 5.0
  109. 12/7/91    dgp    minor editing of comments
  110. 10/10/92 dgp Added support for Pixmap's that require 32-bit addressing.
  111.             Much faster now, using table-lookup instead of sprintf for 
  112.             the hex encoding.
  113.             Deleted obsolete support for THINK C 4.
  114. 4/29/93    dgp    & jas Added explanation of how to do tiling.
  115. 5/27/93    dgp    minor editing for speed and clarity
  116. 6/15/93    dgp    Fixed silly bug introduced 5/27/93 that suppressed all hex data.
  117.             Call StripAddress.
  118. 6/29/93    dgp    Changed call interface to accept a pixmap handle instead of a pointer.
  119.             The problem with accepting a pointer is that the user must remember
  120.             to lock the handle before dereferencing it to get the pointer, and
  121.             lots of people forget, leading to a mysterious crash. Now the locking
  122.             is handled internally, automatically.
  123. 6/30/93    dgp    Added example, above, showing how to print screen to disk.
  124. 7/9/93    dgp check for 32-bit addressing capability.
  125. 12/15/93 dgp added filename argument to the diagnostic messages.
  126.             Corrected grayLevels to grayLevels-1 in computing
  127.             the required number of pixels in the halftone cell.
  128. 6/18/94    dgp    can32 is now computed by calling TrapAvailable(_SwapMMUMode), which 
  129.             returns the correct answer even on Macs with dirty ROMs.
  130. 9/5/94 dgp removed assumption in printf's that int==short.
  131. 5/23/95 dgp Apple changed the prototype in the header file from SwapMMUMode(char *) to 
  132.             SwapMMUMode(signed char *). To retain compatibility with both old and new
  133.             headers, I cast the argument (void *).
  134. 6/30/95 dgp changed TYPE from TEXT to EPSF. Added explanation, above, for how to 
  135.             import into Word.
  136. 6/30/95    dgp add PICT resource preview of image, so we're producing a standard eps file.
  137.             Revised the first several paragraphs of the documentation above to reflect the
  138.             fact that we now produce a standard EPSF file. Make PICT's color table consistent
  139.             with the PostScript.
  140. */
  141. #include "VideoToolbox.h"
  142. #ifndef __TRAPS__
  143.     #include <Traps.h>        // _SwapMMUMode
  144. #endif
  145. #define TYPE 'EPSF'
  146. #define CREATOR 'R*ch'    /* for BBEdit */
  147. PicHandle PixMapToPicture(PixMap **pm,Rect *rectPtr,int pixelSize,ColorTable **cTable);
  148.  
  149. void WindowToEPS(CWindowPtr window,char *filename,Rect *rectPtr
  150.     ,Rect *pageRectPtr,double cellsPerInch,int grayLevels,float reflectance[256])
  151. {
  152.     PixMap **pm;
  153.     FILE *file;
  154.     unsigned char *addr;
  155.     long y,bytes,width;
  156.     register long i;
  157.     unsigned short *buffer,*buffer32;
  158.     register unsigned short *word,hex[256];
  159.     register unsigned char *byte;
  160.     short pixelSize;
  161.     short rowBytes;
  162.     time_t ANSITime;
  163.     char string[100];
  164.     signed char mode;
  165.     char pmState;
  166.     static Boolean can32,is32,firstTime=1;
  167.     long gestalt;
  168.     int error,oldResFile,resFile=0;
  169.     Boolean ok;
  170.     PicHandle pic;
  171.     ColorTable **ct;
  172.     Str63 volumeName="\p";
  173.     ColorSpec *colorSpecPtr;
  174.  
  175.     // Create file's data fork: PostScript text.
  176.     assert(StackSpace()>5000);
  177.     if(firstTime){
  178.         Gestalt(gestaltAddressingModeAttr,&gestalt);
  179.         is32=gestalt&(1L<<gestalt32BitAddressing);
  180.         can32=TrapAvailable(_SwapMMUMode);
  181.         firstTime=0;
  182.     }
  183.     if(IsGWorldPtr(window)){
  184.         // It's a GWorldPtr, lock the pixels.
  185.         ok=LockPixels(GetGWorldPixMap((GWorldPtr)window));
  186.         if(!ok)PrintfExit("%s: can't LockPixels on GWorld.\n",__FILE__);
  187.         pm=GetGWorldPixMap((GWorldPtr)window);
  188.     }else if(window->portVersion<0){    // Is it a CGrafPort or a GrafPort?
  189.         // It's a CGrafPort.
  190.         pm=window->portPixMap;
  191.     }else{
  192.         // It's a GrafPort.
  193.         PrintfExit("%s: source must be GWorld or color Window.\n",__FILE__);
  194.     }
  195.     if(cellsPerInch!=0.0 && grayLevels!=0.0)PrintfExit("%s(%s): "
  196.         "you may not specify BOTH cellsPerInch & grayLevels.\n"
  197.         "Set one to zero.\n",__FILE__,filename);
  198.     file=fopen(filename,"w");
  199.     if(file==NULL)PrintfExit("%s: Error in opening file “%s”.\n"
  200.         ,__FILE__,filename);
  201.     pmState=HGetState((Handle)pm);
  202.     HLock((Handle)pm);
  203.     addr=RectToAddress(*pm,rectPtr,&rowBytes,&pixelSize,NULL);
  204.     if(addr==NULL)PrintfExit("%s(%s): Bad PixMap.\n",__FILE__,filename);
  205.     if(pixelSize!=8)PrintfExit("%s(%s): "
  206.         "Sorry, pixelSize must be 8, not %d.\n",__FILE__,filename,(int)pixelSize);
  207.     time(&ANSITime);
  208.     strftime(string,sizeof(string),"%I:%M %p %A, %B %d, %Y",localtime(&ANSITime));
  209.     fprintf(file,
  210.         "%%!PS-Adobe-2.0 EPSF-1.2\n");
  211.     fprintf(file,
  212.         "%%%%Creator:%s\n",IdentifyApplication());
  213.     fprintf(file,
  214.         "%%%%For:%s\n",IdentifyOwner());
  215.     fprintf(file,
  216.         "%%%%Title:%s\n",filename);
  217.     fprintf(file,
  218.         "%%%%CreationDate:%s\n",string);
  219.     fprintf(file,
  220.         "%%%%Pages: 0\n"
  221.         "%%%%BoundingBox:%d %d %d %d\n"
  222.         ,(int)pageRectPtr->left,(int)pageRectPtr->bottom
  223.         ,(int)pageRectPtr->right,(int)pageRectPtr->top);
  224.     fprintf(file,
  225.         "%%%%EndComments\n"
  226.         "%%%%BeginProlog\n"
  227.         "%%%%EndProlog\n"
  228.         "%%%%BeginSetup\n"
  229.         "/VideoToolbox dup 25 dict def load begin\n"
  230.         "end\n"
  231.         "VideoToolbox begin\n"
  232.         "gsave\n"
  233.         "%%%%EndSetup\n");
  234.     fprintf(file,
  235.         "/nx %d def                %% pixels per raster line\n",(int)(rectPtr->right-rectPtr->left));
  236.     fprintf(file,
  237.         "/ny %d def                %% lines in image\n",(int)(rectPtr->bottom-rectPtr->top));
  238.     fprintf(file,
  239.         "/hypotenuse {dup mul exch dup mul add sqrt} bind def\n"
  240.         "/pixelsPerInch gsave initmatrix 72 0 dtransform hypotenuse grestore def\n"
  241.         "%d %d translate        %% locate lower left of image\n"
  242.         ,(int)pageRectPtr->left,(int)pageRectPtr->bottom);
  243.     fprintf(file,
  244.         "%d %d scale            %% print image with these dimensions on page\n"
  245.         ,(int)(pageRectPtr->right-pageRectPtr->left),(int)(pageRectPtr->top-pageRectPtr->bottom));
  246.     if(cellsPerInch!=0.0){
  247.         fprintf(file,
  248.             "/cellsPerInch %.2f def    %% halftone dot frequency\n",cellsPerInch);
  249.         fprintf(file,
  250.             "cellsPerInch currentscreen 4 -2 roll pop 3 1 roll setscreen\n");
  251.     }
  252.     if(grayLevels!=0){
  253.         fprintf(file,
  254.             "/cellsPerInch pixelsPerInch %.2f div def    %% halftone dot frequency\n"
  255.             ,sqrt(grayLevels-1));
  256.         fprintf(file,
  257.             "cellsPerInch currentscreen 4 -2 roll pop 3 1 roll setscreen\n");
  258.     }
  259.     fprintf(file,
  260.         "/s nx string def        %% string to hold one raster line\n"
  261.         "nx ny 8                    %% dimensions and bits/pixel of source image\n"
  262.         "[nx 0 0 ny neg 0 ny]    %% map unit square to PixMap data\n"
  263.         "{currentfile s readhexstring pop}    %% read data\n"
  264.         "bind                    %% speed up reading\n"
  265.         "image\n");
  266.     assert(sizeof(*byte)==1);    // required by our algorithm
  267.     assert(sizeof(*word)==2);    // required by our algorithm
  268.     if(reflectance==NULL)for(i=0;i<256;i++){
  269.         sprintf(string,"%02x",(int)i);
  270.         hex[i]=*(unsigned short *)string;
  271.     }else for(i=0;i<256;i++){
  272.         int j;
  273.         j=0.5+255*reflectance[i];
  274.         if(j<0)j=0;
  275.         if(j>255)j=255;
  276.         sprintf(string,"%02x",j);
  277.         hex[i]=*(unsigned short *)string;
  278.     }
  279.     width=rectPtr->right-rectPtr->left;
  280.     bytes=(width+1)*sizeof(*word);
  281.     buffer=(unsigned short *)NewPtr(bytes);
  282.     if(buffer==NULL)PrintfExit("%s(%s): "
  283.         "no room for %ld byte buffer.\n\007",__FILE__,filename,bytes);
  284.     buffer32=(unsigned short *)StripAddress(buffer);
  285.     for(y=rectPtr->top;y<rectPtr->bottom;y++){
  286.         mode=true32b;
  287.         if(can32)SwapMMUMode((void *)&mode);
  288.         byte=addr;
  289.         word=buffer32;
  290.         for(i=0;i<width;i++) *word++ = hex[*byte++];
  291.         *word=0;
  292.         if(can32)SwapMMUMode((void *)&mode);
  293.         fprintf(file,"%s\n",(char *)buffer);
  294.         addr+=rowBytes;
  295.     }
  296.     HSetState((Handle)pm,pmState);
  297.     DisposPtr((Ptr)buffer);
  298.     fprintf(file,
  299.         "%%%%Trailer\n"
  300.         "grestore\n"
  301.         "end\n"
  302.         "showpage\n"
  303.         "%%%%EOF\n");
  304.     fclose(file);
  305.     SetFileInfo(filename,TYPE,CREATOR);
  306.  
  307.     // Create file's resource fork: a PICT resource.
  308.     if(1){
  309.         // add PICT resource to file
  310.         FSSpec spec;
  311.         long version;
  312.  
  313.         pixelSize=8;
  314.         ct=GetCTable(pixelSize+32);    // gray ramp
  315.         colorSpecPtr=(**ct).ctTable;
  316.         if(reflectance==NULL){
  317.             for(i=0;i<=(**ct).ctSize;i++){
  318.                 colorSpecPtr->rgb.red=colorSpecPtr->rgb.green=colorSpecPtr->rgb.blue
  319.                     =0xffffL*i/(**ct).ctSize;
  320.                 colorSpecPtr++;
  321.             }
  322.         }else{
  323.             for(i=0;i<=(**ct).ctSize;i++){
  324.                 colorSpecPtr->rgb.red=colorSpecPtr->rgb.green=colorSpecPtr->rgb.blue
  325.                     =0xffffL*reflectance[i];
  326.                 colorSpecPtr++;
  327.             }
  328.         }
  329.         pic=PixMapToPicture(pm,rectPtr,pixelSize,ct);
  330.         DisposeCTable(ct);
  331.         oldResFile=CurResFile();
  332.         error=HGetVol(NULL,&spec.vRefNum,&spec.parID);
  333.         strcpy((char *)spec.name,filename);
  334.         c2pstr((char *)spec.name);
  335.         Gestalt(gestaltSystemVersion,&version);
  336.         if(version>=0x700){
  337.             FSpCreateResFile(&spec,CREATOR,TYPE,smRoman);
  338.             resFile=FSpOpenResFile(&spec,fsRdWrPerm);
  339.         }else{
  340.             HCreateResFile(spec.vRefNum,spec.parID,spec.name);
  341.             resFile=HOpenResFile(spec.vRefNum,spec.parID,spec.name,fsRdWrPerm);
  342.         }
  343.         if(resFile==-1)printf("%s %d: OpenResFile error %d\n",__FILE__,__LINE__,ResError());
  344.         else{
  345.             AddResource((Handle)pic,'PICT',256,"\pPreview");
  346.             UseResFile(oldResFile);
  347.             CloseResFile(resFile);
  348.         }
  349.     }
  350. }
  351.  
  352.